﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;
using System.Text.RegularExpressions;
using System.Web.Hosting;
using System.Web.Security;

using Telerik.Blogs;
using Telerik.Blogs.WebControls;
using Telerik.Cms;
using Telerik.Cms.Data;
using Telerik.Cms.Engine;
using Telerik.Cms.Engine.WebControls;
using Telerik.Events;
using Telerik.Events.WebControls;
using Telerik.Forums;
using Telerik.Libraries;
using Telerik.Libraries.WebControls;
using Telerik.Lists;
using Telerik.Lists.WebControls;

#if !SITEFINITY_37_COMMUNITY
using Telerik.Localization;
#endif

using Telerik.News;
using Telerik.News.WebControls;
using Telerik.Polls;
using Telerik.Security;

using MigrationExportService;

namespace Telerik.Sitefinity.Services.Migration
{
	[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
	public partial class MigrationExportService : IMigrationExportService
	{
		#region Fields

		private ContentManager m_ContentManager;
		private IDictionary<string, ContentProviderBase> m_AllProviders;
		private List<string> m_Cultures;
		private string m_DefaultCulture;

		#endregion

		#region Constructor and private methods

		public MigrationExportService()
		{
			m_ContentManager = new ContentManager();
			m_AllProviders = ContentManager.Providers;
#if !SITEFINITY_37_COMMUNITY
			m_Cultures = LocalizationManager.Cultures.Keys.ToList();
			m_DefaultCulture = LocalizationManager.DefaultCulture.TwoLetterISOLanguageName;
#else
			m_Cultures = new List<string>();
			m_DefaultCulture = "en";
#endif
			//m_Cultures = Localization.Configuration.ConfigHelper.Handler.Cultures.Split(',').Select(c => c.Trim()).ToList();
			/*
			var Manager = new ContentManager("News");
			Manager.MetaKeys.Add(new KeyValuePair<string, IMetaInfo>("Location", new MetaInfo("Location", MetaValueTypes.ShortText)));
			var cnt = Manager.CreateContent("text/html");
			cnt.SetMetaData("Location", "Sofia");
			cnt.Content = "Sofia";
			Manager.SaveContent(cnt);
			*/
		}

		#endregion

		#region Content methods

		private void AddContentComments(List<MigrationComment> commentsList, IContent content)
		{
			foreach (IComment comment in content.Comments)
			{
				MigrationComment mc = new MigrationComment();
				mc.Author = comment.Author;
				mc.Email = comment.Email;
				mc.Id = comment.ID;
				mc.IpAddress = comment.IpAddress;
				mc.Owner = comment.Owner;
				mc.Text = comment.Text;
				mc.Visible = comment.Visible;
				mc.WebSite = comment.WebSite;

				commentsList.Add(mc);
			}
		}

		private void AddMetaFields(Dictionary<string, object> metaDataList, IContent content, string providerName)
		{
			ContentManager cm = new ContentManager(providerName);
			IDictionary<string, IMetaInfo> metaKeys = cm.GetMetaKeys(content);

			foreach (IMetaInfo value in metaKeys.Values)
			{
				metaDataList[value.Key] = content.GetMetaData(value.Key);
			}
		}

		private void AddContentTags(List<string> tagList, Guid contentItemID)
		{
			IList tags = m_ContentManager.GetTags(contentItemID);
			foreach (ITag tag in tags)
			{
				tagList.Add(tag.TagName);
			}
		}

		private void GetSingleGenericContentItem(MigrationGenericContent mgc, IContent item, string providerName, string culture)
		{
			if (culture != m_DefaultCulture)
				item.Language = culture;

			mgc.Content = item.Content;
			mgc.DateCreated = item.DateCreated;
			mgc.DateModified = item.DateModified;
			mgc.Id = item.ID;
			mgc.ItemInfo = item.ItemInfo;
			mgc.Language = culture;
			mgc.MimeType = item.MimeType;
			mgc.ProviderName = item.ProviderName;
			mgc.Status = item.Status.ToString();
			mgc.Url = item.Url;

			AddContentComments(mgc.Comments, item);
			AddMetaFields(mgc.MetaData, item, providerName);
			AddContentTags(mgc.Tags, item.ID);
		}

		private void AddGenericContentToList(IList items, List<MigrationGenericContent> output, string providerName, string culture)
		{
			foreach (IContent item in items)
			{
				MigrationGenericContent mgc = new MigrationGenericContent();
				GetSingleGenericContentItem(mgc, item, providerName, culture);
				output.Add(mgc);
			}
		}

		private void AddGenericContentGuidToList(IList items, List<MigrationItemContentDefinition> output, string providerName, string culture)
		{
			foreach (var item in items)
			{
				MigrationItemContentDefinition micd = new MigrationItemContentDefinition();
				if (item is IContent)
				{
					micd.Id = ((IContent)item).ID;
				}
				else if (item is IBlog)
				{
					micd.Id = ((IBlog)item).ID;
				}
				else if (item is IEvent)
				{
					micd.Id = ((IEvent)item).ID;
				}
				else if (item is ILibrary)
				{
					micd.Id = ((ILibrary)item).ID;
				}
				micd.ProviderName = providerName;
				micd.Language = culture;
				output.Add(micd);
			}
		}

		#endregion

		#region Controls

		#region GetMigrationControl

		private static void FillControlBaseFields(MigrationCmsWebControl migrationControl, ICmsWebControl control)
		{
			migrationControl.ContainerId = control.ContainerID;
			try
			{
				migrationControl.ControlTypeName = control.ControlType.ToString();
			}
			catch (ControlNotResolvedException) { }
			migrationControl.Id = control.ID;
			migrationControl.IsAscx = control.IsAscx;
			migrationControl.ItemType = control.ItemType;
			migrationControl.Name = control.Name;
			migrationControl.Ordinal = control.Ordinal;
			migrationControl.TypeName = control.TypeName;
		}

		private MigrationCmsWebControl GetMigrationControl(ICmsWebControl control)
		{
			Type controlType;

			try
			{
				controlType = control.ControlType;
			}
			catch (ControlNotResolvedException ex)
			{
				MigrationCmsWebControl result = new MigrationCmsWebControl();
				FillControlBaseFields(result, control);
				result.ErrorMessage = ex.Message;

				return result;
			}

			if (control.IsAscx)
			{
				MigrationCmsAscxControl result = new MigrationCmsAscxControl();
				FillControlBaseFields(result, control);

				string userControlPath = control.TypeName;
				string userControlPathResolved = HostingEnvironment.MapPath(userControlPath);
				string userControlContents = MigrationUtils.GetSafeTextFileContents(userControlPathResolved);
				string userControlCodeBehindContents = String.Empty;

				Match codeBehindMatch = Regex.Match(userControlContents, @"<%@\s*Control.*?CodeFile=""?([\w\._\/~-]*)""?.*?%>", RegexOptions.IgnoreCase | RegexOptions.Singleline);
				if (codeBehindMatch.Success)
				{
					var codeBehindFileName = codeBehindMatch.Groups[1].Value;
					result.AscxCodeBehindFileName = codeBehindFileName;
					var codeBehindFullPath = Path.GetDirectoryName(userControlPathResolved) + "\\" + codeBehindFileName;

					userControlCodeBehindContents = MigrationUtils.GetSafeTextFileContents(codeBehindFullPath);
				}

				result.AscxFilePath = userControlPath;
				result.AscxFileContent = userControlContents;
				result.AscxFileCondeBehindContent = userControlCodeBehindContents;

				return result;
			}
			else if (controlType == typeof(Telerik.Cms.Engine.WebControls.GenericContent))
			{
				MigrationCmsGenericContent result = new MigrationCmsGenericContent();
				FillControlBaseFields(result, control);

				GenericContent genericContent = control.LoadControl() as GenericContent;
				result.Content = genericContent.Content;

				return result;
			}
			else if (controlType == typeof(System.Web.UI.WebControls.Image))
			{
				MigrationCmsImage result = new MigrationCmsImage();
				FillControlBaseFields(result, control);

				System.Web.UI.WebControls.Image imageControl = control.LoadControl() as System.Web.UI.WebControls.Image;
				byte[] imageData = { };
				try
				{
					imageData = MigrationUtils.GetSafeBinaryFileContents(HostingEnvironment.MapPath(imageControl.ImageUrl));
				}
				catch (Exception) { }

				result.ImageData = imageData;
				result.ImageAlign = imageControl.ImageAlign.ToString();
				result.ImageUrl = imageControl.ImageUrl;
				result.AlternateText = imageControl.AlternateText;

				return result;
			}
			else if (controlType == typeof(Telerik.Cms.Engine.WebControls.SitefinityImage))
			{
				MigrationCmsImage result = new MigrationCmsImage();
				FillControlBaseFields(result, control);

				SitefinityImage sitefinityImage = control.LoadControl() as SitefinityImage;
				byte[] imageData = { };
				try
				{
					imageData = MigrationUtils.GetSafeBinaryFileContents(HostingEnvironment.MapPath(sitefinityImage.ImageUrl));
				}
				catch (Exception) { }

				result.ImageData = imageData;
				result.ImageAlign = sitefinityImage.ImageAlign.ToString();
				result.ImageUrl = sitefinityImage.ImageUrl;
				result.AlternateText = sitefinityImage.AlternateText;

				return result;
			}
			// This control type is obsolete but it's used everywhere
			else if (controlType == typeof(Telerik.Cms.Web.UI.DynamicHyperLink))
			{
				MigrationCmsDynamicHyperLink result = new MigrationCmsDynamicHyperLink();
				FillControlBaseFields(result, control);

				try
				{
					Telerik.Cms.Web.UI.DynamicHyperLink dynamicHyperLink = control.LoadControl() as Telerik.Cms.Web.UI.DynamicHyperLink;
					result.NavigateUrl = dynamicHyperLink.NavigateUrl;
					result.Target = dynamicHyperLink.Target;
					result.Text = dynamicHyperLink.Text;
				}
                catch (Exception) { };

				return result;
			}
			else if (controlType == typeof(Telerik.Cms.Engine.WebControls.DynamicHyperLink))
			{
				MigrationCmsDynamicHyperLink result = new MigrationCmsDynamicHyperLink();
				FillControlBaseFields(result, control);

				try
				{
					Telerik.Cms.Engine.WebControls.DynamicHyperLink dynamicHyperLink = control.LoadControl() as Telerik.Cms.Engine.WebControls.DynamicHyperLink;
					result.NavigateUrl = dynamicHyperLink.NavigateUrl;
					result.Target = dynamicHyperLink.Target;
					result.Text = dynamicHyperLink.Text;
				}
                catch (Exception) { };

				return result;
			}
			else if (controlType == typeof(System.Web.UI.WebControls.HyperLink))
			{
				MigrationCmsHyperLink result = new MigrationCmsHyperLink();
				FillControlBaseFields(result, control);

				try
				{
					System.Web.UI.WebControls.HyperLink hyperLink = control as System.Web.UI.WebControls.HyperLink;
					result.NavigateUrl = hyperLink.NavigateUrl;
					result.Target = hyperLink.Target;
					result.Text = hyperLink.Text;
				}
				catch (Exception) { };

				return result;
			}
			else if (controlType == typeof(BlogPosts))
			{
				MigrationCmsBlogPosts result = new MigrationCmsBlogPosts();
				FillControlBaseFields(result, control);

				try
				{
					BlogPosts blogPosts = control.LoadControl() as BlogPosts;
				}
                catch (Exception) { }

				return result;
			}
			else if (controlType == typeof(Telerik.Events.WebControls.EventsView))
			{
				MigrationCmsEventsView result = new MigrationCmsEventsView();
				FillControlBaseFields(result, control);

				try
				{
					EventsView eventsView = control.LoadControl() as EventsView;
				}
				catch (Exception) { }

				return result;
			}
			else if (controlType == typeof(Telerik.News.WebControls.NewsView))
			{
				MigrationCmsNewsView result = new MigrationCmsNewsView();
				FillControlBaseFields(result, control);

				try
				{
					NewsView newsView = control.LoadControl() as NewsView;
				}
                catch (Exception) { }

				return result;
			}
			else if (controlType == typeof(Telerik.Libraries.DownloadList))
			{
				MigrationCmsDownloadList result = new MigrationCmsDownloadList();
				FillControlBaseFields(result, control);

				try
				{
					DownloadList downloadList = control.LoadControl() as DownloadList;
				}
                catch (Exception) { }

				return result;
			}
			else if (controlType == typeof(Telerik.Libraries.WebControls.ImageGallery))
			{
				MigrationCmsImageGallery result = new MigrationCmsImageGallery();
				FillControlBaseFields(result, control);

				try
				{
					ImageGallery imageGallery = control.LoadControl() as ImageGallery;
				}
                catch (Exception) { }

				return result;
			}
			else if (controlType == typeof(Telerik.Lists.WebControls.ListDisplay))
			{
				MigrationCmsListDisplay result = new MigrationCmsListDisplay();
				FillControlBaseFields(result, control);

				try
				{
					ListDisplay listDisplay = control.LoadControl() as ListDisplay;
					result.ListIds = listDisplay.ListsIds.ToList();
				}
				catch (Exception) { }

				return result;
			}
			else if (controlType == typeof(Telerik.Notifications.Newsletters.WebControl.SubscriptionForm))
			{
				MigrationCmsSubscriptionForm result = new MigrationCmsSubscriptionForm();
				FillControlBaseFields(result, control);

				return result;
			}
			else if (controlType == typeof(Telerik.Search.WebControls.SearchBox))
			{
				MigrationCmsSearchBox result = new MigrationCmsSearchBox();
				FillControlBaseFields(result, control);

				return result;
			}
			else
			{
				MigrationCmsWebControl result = new MigrationCmsWebControl();
				FillControlBaseFields(result, control);

				return result;
			}
		}

		#endregion

		#region GetControlsToMigrationCollection

		public void GetControlsToMigrationCollection(IList<ICmsWebControl> controls, List<MigrationCmsWebControl> migrationControls)
		{
			foreach (ICmsWebControl control in controls)
			{
				MigrationCmsWebControl mcwc = GetMigrationControl(control);

				migrationControls.Add(mcwc);
			}
		}

		#endregion

		#endregion

		#region Ping

		public string Ping()
		{
			return "Pong";
		}

		#endregion

		#region GetMigrationItemsCounts

		public Message GetMigrationStatistics()
		{
			var mic = new MigrationItemStatistics();

			mic.ContentLanguages = m_Cultures;
			mic.DefaultLanguage = LocalizationManager.DefaultCulture.Name;

			// Roles count
			RoleProviderCollection providers = Roles.Providers;
			foreach (RoleProvider provider in providers)
			{
				string providerName = provider.Name;
				UserManager userManager = new UserManager(providerName);
				mic.RolesCount += userManager.GetAllRoles().Length;
			}

			// Users count
			MembershipProviderCollection membershipProviders = Membership.Providers;
			foreach (MembershipProvider provider in membershipProviders)
			{
				string providerName = provider.Name;
				UserManager userManager = new UserManager(providerName);

				int outRows;
				try
				{
					MembershipUserCollection users = userManager.GetAllUsers(0, 0, "Email ASC", out outRows);
				}
				catch (Exception)
				{
					outRows = 0;
				}
				mic.UsersCount += outRows;
			}

			foreach (KeyValuePair<string, ContentProviderBase> provider in m_AllProviders)
			{
				// Generic contents count
				if (provider.Value.GetType().ToString() == "Telerik.Cms.Engine.Data.Providers.DefaultProvider")
				{
					string providerName = provider.Key;
					ContentManager contentManager = new ContentManager(providerName);
					mic.GenericContentsCount += contentManager.ContentCount();
				}

				// News count
				if (provider.Value is Telerik.News.Data.DefaultNewsProvider)
				{
					string providerName = provider.Key;
					NewsManager newsManager = new NewsManager(providerName);
					mic.NewsCount += newsManager.Content.ContentCount();
				}

				// Blogs count
				if (provider.Value is Telerik.Blogs.Data.DefaultBlogProvider)
				{
					string providerName = provider.Key;
					BlogManager blogManager = new BlogManager(providerName);
					mic.BlogsCount += blogManager.GetBlogs().Count;
					mic.BlogPostsCount += blogManager.Content.ContentCount();
				}

				// Events count
				if (provider.Value is Telerik.Events.Data.DefaultEventsProvider)
				{
					string providerName = provider.Key;
					EventsManager eventsManager = new EventsManager(providerName);
					mic.EventsCount += eventsManager.ContentCount();
				}

				// Libraries count
				if (provider.Value is Telerik.Libraries.Data.DefaultProvider)
				{
					string providerName = provider.Key;
					LibraryManager libraryManager = new LibraryManager(providerName);
					IList libraryList = libraryManager.GetAllLibraries(0, 0, "Name ASC");
					foreach (ILibrary libraryItem in libraryList)
					{
						if (libraryItem.TypeName == "Image")
							mic.ImageLibrariesCount++;
						else if (libraryItem.TypeName == "Document")
							mic.DocumentLibrariesCount++;
						else
							mic.VideoLibrariesCount++;
					}
				}
			}

			// Lists count
			IDictionary<string, ListProvider> listProviders = ListManager.Providers;
			foreach (KeyValuePair<string, ListProvider> listProvider in listProviders)
			{
				string providerName = listProvider.Key;
				ListManager listManager = new ListManager(providerName);
				mic.ListsCount += listManager.GetLists().Count;
			}

			// Polls count
			IDictionary<string, PollsProvider> pollsProviders = PollManager.Providers;
			foreach (KeyValuePair<string, PollsProvider> pollsProvider in pollsProviders)
			{
				string providerName = pollsProvider.Key;
				PollManager pollManager = new PollManager(providerName);
				mic.PollsCount += pollManager.GetAllPolls().Count;
			}

			// Forums count
			IDictionary<string, ForumProvider> forumProviders = ForumManager.Providers;
			foreach (KeyValuePair<string, ForumProvider> forumProvider in forumProviders)
			{
				string providerName = forumProvider.Key;
				ForumManager forumManager = new ForumManager(providerName);
				mic.ForumsCount += forumManager.GetForums(false).Count;
			}

			// Master pages count
			string[] masterPages = { };
			try
			{
				masterPages = Directory.GetFiles(HostingEnvironment.MapPath("~/App_Master"), "*.master");
			}
			catch (IOException) { }
			catch (UnauthorizedAccessException) { };
			mic.MasterPagesCount = masterPages.Length;

			// Themes count
			string[] themes = { };
			try
			{
				themes = Directory.GetDirectories(HostingEnvironment.MapPath("~/App_Themes"));
			}
			catch (IOException) { }
			catch (UnauthorizedAccessException) { };
			mic.ThemesCount = themes.Length;

			// Templates count
			IDictionary<string, CmsProvider> cmsProviders = CmsManager.Providers;
			foreach (KeyValuePair<string, CmsProvider> cmsProvider in cmsProviders)
			{
				string providerName = cmsProvider.Key;

#if VERSION_BELOW_SP3
				CmsManager cmsManager = new CmsManager();
#else
				CmsManager cmsManager = new CmsManager(providerName);			
#endif

				mic.TemplatesCount += cmsManager.GetTemplates().Count;
			}

			// Pages count				
			foreach (KeyValuePair<string, CmsProvider> cmsProvider in cmsProviders)
			{
				string providerName = cmsProvider.Key;

#if VERSION_BELOW_SP3
				CmsManager cmsManager = new CmsManager();
#else
				CmsManager cmsManager = new CmsManager(providerName);
#endif

				mic.PagesCount += cmsManager.GetPages().Count;
			}

			MessageVersion ver = OperationContext.Current.IncomingMessageVersion;
			return Message.CreateMessage(ver, "GetMigrationStatistics", mic);
		}

		#endregion

		#region GetContentProviders

		public Message GetContentProviders()
		{
			List<MigrationProvider> output = new List<MigrationProvider>();

			foreach (KeyValuePair<string, ContentProviderBase> provider in m_AllProviders)
			{
				MigrationProvider mp = new MigrationProvider();
				mp.Application = provider.Value.Application;
				mp.AllowComments = provider.Value.AllowComments;
				mp.AllowLocalization = provider.Value.AllowLocalization;
				mp.AllowVersioning = provider.Value.AllowVersioning;
				mp.AllowWorkflow = provider.Value.AllowWorkflow;
				mp.CommentsModeration = provider.Value.CommentsModeration;
				mp.DefaultMetaField = provider.Value.DefaultMetaField;
				mp.Name = provider.Value.Name;
				mp.Type = provider.Value.GetType().ToString();
				mp.UrlRewriteFormat = provider.Value.UrlRewriteFormat;
				mp.UrlDateTimeFormat = provider.Value.UrlDateTimeFormat;
				mp.UrlWhiteSpaceChar = provider.Value.UrlWhitespaceChar;
				mp.Visible = provider.Value.Visible;

				IDictionary<string, IMetaInfo> metaKeys = provider.Value.MetaKeys;

				foreach (IMetaInfo metaKey in metaKeys.Values)
				{
					MigrationMetaDataDefinition mmdd = new MigrationMetaDataDefinition();
					mmdd.Caption = metaKey.Caption;
					mmdd.DefaultValue = metaKey.DefaultValue;
					mmdd.Key = metaKey.Key;
					mmdd.Localizable = metaKey.Localizable;
					mmdd.Mandatory = metaKey.Mandatory;
					mmdd.Searchable = metaKey.Searchable;
					mmdd.Sortable = metaKey.Sortable;
					mmdd.ValueType = metaKey.ValueType.ToString();
					mmdd.Visible = metaKey.Visible;

					mp.MetaDataDefinitions.Add(mmdd);
				}

				output.Add(mp);
			}

			MessageVersion ver = OperationContext.Current.IncomingMessageVersion;
			return Message.CreateMessage(ver, "GetContentProvidersResponse", output);
		}

		#endregion

		#region GetContentForProvider

		public Message GetContentForProvider(Message providerParam)
		{
			MigrationProvider mp = providerParam.GetBody<MigrationProvider>();
			string providerName = mp.Name;

			ContentManager contentManager = new ContentManager(providerName);

			List<MigrationGenericContent> output = new List<MigrationGenericContent>();

			if (contentManager.Provider.AllowLocalization)
			{
				foreach (var culture in m_Cultures)
				{
					System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo(culture);

					IList contentItems = contentManager.GetContent();
					AddGenericContentToList(contentItems, output, providerName, culture);
				}
			}
			else
			{
				IList contentItems = contentManager.GetContent();
				AddGenericContentToList(contentItems, output, providerName, String.Empty);
			}

			MessageVersion ver = OperationContext.Current.IncomingMessageVersion;
			return Message.CreateMessage(ver, "GetContentForProviderResponse", output);
		}

		#endregion
	}

	static class Extensions
	{
		public static bool EndsWithAny(this string inputString, IEnumerable<string> endings)
		{
			return endings.Any(inputString.EndsWith);
		}
	}
}
